<?php
declare(strict_types=1);

namespace ExpressionEngine\LexicalAnalysisEngine;

use Contract\Exceptions\LogicException;
use Contract\Exceptions\ValidationException;
use ExpressionEngine\LexicalAnalysisEngine\Enum\LexicalAnalysisEnum;
use ExpressionEngine\Utils\StringUtil;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;

class LexicalAnalysisEngine implements LexicalAnalysisEngineInterface
{
    protected LexicalAnalysisLoop $lexicalAnalysisLoop;
    protected LexicalAnalysisExpression $lexicalAnalysisExpression;
    protected LexicalAnalysisSymbol $lexicalAnalysisSymbol;

    public function __construct(LexicalAnalysisLoop $lexicalAnalysisLoop, LexicalAnalysisExpression $lexicalAnalysisExpression, LexicalAnalysisSymbol $lexicalAnalysisSymbol)
    {
        $this->lexicalAnalysisLoop = $lexicalAnalysisLoop;
        $this->lexicalAnalysisExpression = $lexicalAnalysisExpression;
        $this->lexicalAnalysisSymbol = $lexicalAnalysisSymbol;
    }

    /**
     * @param string $expression
     * @return array
     * @throws LogicException
     * @throws ValidationException
     */
    public function scanning(string $expression): array
    {
        if (empty($expression)) {
            throw new ValidationException('expression is empty');
        }
        $data = [
            LexicalAnalysisEnum::SCAN_KEY_EXPRESSION_NAME => $expression
        ];
        $data = array_merge($data, $this->scan($data[LexicalAnalysisEnum::SCAN_KEY_EXPRESSION_NAME]));
        foreach ($data[LexicalAnalysisEnum::SCAN_KEY_LOOP_LIST_NAME] as $key => $loopItem) {
            $data[LexicalAnalysisEnum::SCAN_KEY_LOOP_LIST_NAME][$key] = array_merge($loopItem, $this->scan($loopItem[LexicalAnalysisEnum::SCAN_KEY_EXPRESSION_NAME]));
        }
        return $data;
    }

    /**
     * @param string $expression
     * @return array
     * @throws LogicException
     * @throws ValidationException
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    protected function scan(string $expression): array
    {
        $data = [];
        $expression = StringUtil::formatExpression($expression);
        $data[LexicalAnalysisEnum::SCAN_KEY_LOOP_LIST_NAME] = $this->lexicalAnalysisLoop->scan($expression);
        $childExpressionList = $this->lexicalAnalysisExpression->scan($expression);
        $symbolList = [];
        foreach ($childExpressionList as $key => $childExpression) {
            $childExpressionList[$key] = $this->lexicalAnalysisSymbol->scan($childExpression, $symbolList);
        }
        $data[LexicalAnalysisEnum::SCAN_KEY_EXPRESSION_LIST_NAME] = $childExpressionList;
        $data[LexicalAnalysisEnum::SCAN_KEY_VARIABLE_LIST_NAME] = $symbolList[LexicalAnalysisEnum::SCAN_KEY_VARIABLE_LIST_NAME];
        $data[LexicalAnalysisEnum::SCAN_KEY_CONSTANT_LIST_NAME] = $symbolList[LexicalAnalysisEnum::SCAN_KEY_CONSTANT_LIST_NAME];
        return $data;
    }
}